﻿#region using

using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

#endregion

namespace JupiterLibrary
{
    public enum EstadosCarrusel
    {
        NoInicializado,
        Iniciando,
        TerminaDeLlegar,
        Reposo,
        Saliendo
    }

    public class ElementoCarrusel
    {
        // La posición es en View Space
        public Vector3 Position;
        public Vector3 PositionFinal
        {
            get
            {
                float radianes = MathHelper.ToRadians(Angulo);

                Vector3 p = new Vector3((float)Math.Cos(radianes) * Radio, 0, (float)Math.Sin(radianes) * Radio);

                // Debo centrar el círculo justo enfrente de la cámara
                p += new Vector3(0, 0, -10);

                return p;
            }
        }
        public Texture2D Texture;
        public Sprite Sprite;
        public float Angulo;
        public float Radio;
        public float AlphaSalir;

        public void NormalizarAngulo()
        {
            if (Angulo < 0) Angulo += 360;
            if (Angulo >= 360) Angulo -= 360;

        }
    }

    public class Carrusel
    {
        public static SoundEffect sound;

        public List<Texture2D> Texturas;

        public EstadosCarrusel Estado;

        List<ElementoCarrusel> elementos;

        float VelocidadRotacion;

        Matrix cameraView;

        bool soundPlaying;

        public Carrusel()
        {
            Texturas = new List<Texture2D>();

            Estado = EstadosCarrusel.NoInicializado;

            if (sound == null)
            {
                sound = Jupiter.Content.Load<SoundEffect>("music/PasarFoto");
            }
        }

        public void Start()
        {
            soundPlaying = false;

            Estado = EstadosCarrusel.Iniciando;

            elementos = new List<ElementoCarrusel>();

            float grados = 360 / Texturas.Count;
            float radio=3;
            float angulo=0;

            foreach (Texture2D texture in Texturas)
            {
                ElementoCarrusel elemento = new ElementoCarrusel();
                elemento.Texture = texture;
                elemento.Position = new Vector3(0, 10, -20);
                elemento.Sprite = new Sprite();
                elemento.Sprite.UseMatrix = true;
                elemento.Sprite.SetRectangle(new Rectangle(-1, -1, 2, 2));
                elemento.Sprite.Texture = texture;
                elemento.Sprite.InvertirCoordsTextura(false, true);
                elemento.Sprite.Update();

                elemento.Angulo = angulo;
                elemento.Radio = radio;
                elemento.AlphaSalir = 0;

                elementos.Add(elemento);

                angulo+=grados;
            }
            //VelocidadRotacion = 30;
            VelocidadRotacion = 100;

            cameraView = Matrix.CreateLookAt(new Vector3(0,0,3), new Vector3(0, 0, -1), Vector3.Up);
        }

        public void End()
        {
            Estado = EstadosCarrusel.Saliendo;
        }

        void Salir()
        {
            float inc=Jupiter.AtXSeconds(1,1);

            foreach (ElementoCarrusel elemento in elementos)
            {
                elemento.AlphaSalir += inc;

                if (elemento.AlphaSalir > 1)
                    Estado = EstadosCarrusel.NoInicializado;
            }

            if (Estado == EstadosCarrusel.NoInicializado) elementos = null;
        }

        void MoverEnCirculo(bool actualizarVelocidad)
        {
            if (VelocidadRotacion == 0) return;

            float inc = Jupiter.At1Second(VelocidadRotacion);

            if (actualizarVelocidad)
            {
                //VelocidadRotacion -= Jupiter.AtXSeconds(1, 10);
                VelocidadRotacion -= Jupiter.AtXSeconds(0.05f, 10);

                if (VelocidadRotacion <= 3)
                {
                    VelocidadRotacion = 0;

                    Estado = EstadosCarrusel.Reposo;

                    MoverIzquierda();

                    return;
                }
            }

            foreach (ElementoCarrusel elemento in elementos)
            {
                elemento.Angulo += inc;

                if (elemento.Angulo >= 360) elemento.Angulo -= 360;
            }
        }

        ElementoCarrusel elementoQueIraAlFrente;
        float anguloOrigen;
        bool movIzquierda;

        void MoverParaCentrar()
        {
            if (elementoQueIraAlFrente == null) return;

            float inc = Jupiter.AtXSeconds(1, 90 - anguloOrigen);
            //float inc = Jupiter.AtXSeconds(0.001f, 90 - anguloOrigen);

            foreach (ElementoCarrusel elemento in elementos)
            {
                elemento.Angulo += inc;

                elemento.NormalizarAngulo();
            }

            if (movIzquierda)
            {
                if (elementoQueIraAlFrente.Angulo > 90 && elementoQueIraAlFrente.Angulo < 180)
                {
                    TerminarRotacion(90 - elementoQueIraAlFrente.Angulo);
                }
            }
            else
            {
                if (elementoQueIraAlFrente.Angulo < 90)
                {
                    TerminarRotacion(90 - elementoQueIraAlFrente.Angulo);
                }
            }

        }

        void TerminarRotacion(float inc)
        {
            foreach (ElementoCarrusel elemento in elementos)
            {
                elemento.Angulo += inc;

                elemento.NormalizarAngulo();
            }
            /*
            elementoQueIraAlFrente.Sprite.SetColor(Color.White);

            elementoQueIraAlFrente.Sprite.Update();
            */
            elementoQueIraAlFrente = null;
        }

        public void MoverDerecha()
        {
            if (elementoQueIraAlFrente != null) return;

            // Calculo el que tengo cerca lo que le queda para llegar a 90ª

            anguloOrigen = 360;

            foreach (ElementoCarrusel elemento in elementos)
            {
                if (elemento.Angulo > 90 && elemento.Angulo < anguloOrigen)
                {
                    elementoQueIraAlFrente = elemento;

                    anguloOrigen = elemento.Angulo;
                }

            }
            /*
            elementoQueIraAlFrente.Sprite.SetColor(Color.Red);

            elementoQueIraAlFrente.Sprite.Update();
            */
            movIzquierda = false;
        }

        public void MoverIzquierda()
        {
            if (elementoQueIraAlFrente != null) return; 
            
            // Calculo el que tengo cerca lo que le queda para llegar a 90ª

            anguloOrigen = -180;
            

            foreach (ElementoCarrusel elemento in elementos)
            {
                float angulo = elemento.Angulo;

                if (angulo >= 180) angulo -= 360;

                if (angulo < 90 && angulo > anguloOrigen)
                {
                    elementoQueIraAlFrente = elemento;
                    
                    anguloOrigen = angulo;
                }

            }
            /*
            elementoQueIraAlFrente.Sprite.SetColor(Color.Red);

            elementoQueIraAlFrente.Sprite.Update();
            */
            movIzquierda = true;
        }

        public void Update()
        {
            if (elementos == null) return;

            switch (Estado)
            {

                case EstadosCarrusel.Iniciando:

                    MoverEnCirculo(false);

                    int enReposo = 0;

                    foreach (ElementoCarrusel elemento in elementos)
                    {
                        Vector3 dir = elemento.PositionFinal - elemento.Position;

                        if (dir.Length() < 0.1f)
                        {
                            elemento.Position = elemento.PositionFinal;

                            enReposo++;

                        }
                        else
                        {
                            dir.Normalize();

                            //elemento.Position += dir * Jupiter.At1Second(5);
                            elemento.Position += dir * Jupiter.At1Second(20);
                        }
                    }

                    if (enReposo == elementos.Count)
                        Estado = EstadosCarrusel.TerminaDeLlegar;

                    break;

                case EstadosCarrusel.TerminaDeLlegar:

                    MoverEnCirculo(true);

                    foreach (ElementoCarrusel elemento in elementos)
                    {
                        elemento.Position = elemento.PositionFinal;
                    }

                    break;

                case EstadosCarrusel.Reposo:

                    MoverParaCentrar();

                    foreach (ElementoCarrusel elemento in elementos)
                    {
                        elemento.Position = elemento.PositionFinal;
                    }

                    break;

                case EstadosCarrusel.Saliendo:

                    Salir();

                    break;
            }

        }

        public void Draw()
        {
            if (elementos == null) return;

            Jupiter.Video.DrawBeforeSprites();
            Jupiter.Video.GraphicsDevice.DepthStencilState = DepthStencilState.Default;
            Jupiter.Video.GraphicsDevice.RasterizerState = RasterizerState.CullNone;

            // Pasamos a World Space
            Matrix viewToWorld=Jupiter.Video.GlobalRender.Camera.World;
            
            AlphaTestEffect effect = Jupiter.Video.AlphaTestEffect;

            effect.View = cameraView;
            effect.Projection = Jupiter.Video.GlobalRender.Camera.Projection;
            effect.VertexColorEnabled = true;
            effect.ReferenceAlpha = 10;

            foreach (ElementoCarrusel elemento in elementos)
            {
                float scale = 1;

                // Rango de -7 a -13. Lo normalizamos de 0 a 1

                //float a= (elemento.Position.Z + 13)/6f;
                float a = (elemento.Position.Z + 13) / 3f;
                // Ahora limitamos de 0.5 a 1

                if (a < 0.5f) a = 0.5f;

                effect.Alpha = a - elemento.AlphaSalir;

                // La escala depende de si estoy enfrente

                if (Estado == EstadosCarrusel.Reposo)
                {

                    int distanciaAnguloDondeEmpiezaElEfecto = 20;
                    float factorScale = 1.5f;
                    
                    if (elemento.Angulo >= 90 - distanciaAnguloDondeEmpiezaElEfecto && elemento.Angulo <= 90)
                    {
                        if (!soundPlaying)
                        {
                            sound.Play();
                            soundPlaying = true;
                        }
                        scale += (float)(Math.Abs(elemento.Angulo - (90 - distanciaAnguloDondeEmpiezaElEfecto)) / 10) * factorScale;
                    }
                    else if (elemento.Angulo >= 90 && elemento.Angulo <= 90 + distanciaAnguloDondeEmpiezaElEfecto)
                    {
                        soundPlaying = false;

                        scale += (float)(Math.Abs((90 + distanciaAnguloDondeEmpiezaElEfecto) - elemento.Angulo) / 10) * factorScale;
                    }
                }

                elemento.Sprite.Matrix = Matrix.CreateScale(scale) * Matrix.CreateTranslation(elemento.Position);

                elemento.Sprite.Draw(effect);
            }
             
        }
    }
}
